// -*- coding: utf-8 -*-
/*
author: zengbin93
email: zeng_bin8888@163.com
create_dt: 2022/12/5 19:02
describe: 飞书应用API接口封装
*/

use log;
use reqwest::Client;
use serde_json::Value;
use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::time::{sleep, Duration};

/// 飞书API标准请求
/// 
/// # Arguments
/// * `method` - 请求方法
/// * `url` - 请求地址
/// * `headers` - 请求头
/// * `payload` - 传参
pub async fn request(
    method: &str,
    url: &str,
    headers: &HashMap<String, String>,
    payload: Option<&Value>
) -> Result<Value, Box<dyn std::error::Error>> {
    let client = Client::new();
    let mut request_builder = match method.to_uppercase().as_str() {
        "GET" => client.get(url),
        "POST" => client.post(url),
        "PUT" => client.put(url),
        "DELETE" => client.delete(url),
        _ => return Err("不支持的HTTP方法".into()),
    };
    
    // 添加请求头
    for (key, value) in headers {
        request_builder = request_builder.header(key, value);
    }
    
    // 添加请求体
    if let Some(payload) = payload {
        request_builder = request_builder.json(payload);
    }
    
    let response = request_builder.send().await?;
    
    log::info!("{}", "+".repeat(88));
    log::info!("URL: {} || X-Tt-Logid: {:?}", url, response.headers().get("X-Tt-Logid"));
    log::info!("headers: {:?}", headers);
    log::info!("payload: {:?}", payload);
    
    let response_text = response.text().await?;
    let resp: Value = if response_text.starts_with('{') {
        serde_json::from_str(&response_text)?
    } else {
        log::info!("response: {}", response_text);
        return Err("响应不是有效的JSON".into());
    };
    
    log::info!("response: {:?}", resp);
    
    let code = resp.get("code")
        .and_then(|v| v.as_i64())
        .unwrap_or_else(|| resp.get("StatusCode").and_then(|v| v.as_i64()).unwrap_or(-1));
    
    if code == -1 && response.status() != reqwest::StatusCode::OK {
        return Err(format!("HTTP错误: {}", response.status()).into());
    }
    
    if code != 0 {
        let msg = resp.get("msg").and_then(|v| v.as_str()).unwrap_or("");
        log::debug!("request fail: code={}, msg={}", code, msg);
        return Err(format!("request fail: code={}, msg={}", code, msg).into());
    }
    
    Ok(resp)
}

/// 飞书API基础类
pub struct FeishuApiBase {
    pub app_id: String,
    pub app_secret: String,
    pub host: String,
    pub headers: HashMap<String, String>,
    pub cache: HashMap<String, Value>,
}

impl FeishuApiBase {
    pub fn new(app_id: &str, app_secret: &str) -> Self {
        let mut headers = HashMap::new();
        headers.insert("Content-Type".to_string(), "application/json".to_string());
        
        Self {
            app_id: app_id.to_string(),
            app_secret: app_secret.to_string(),
            host: "https://open.feishu.cn".to_string(),
            headers,
            cache: HashMap::new(),
        }
    }
    
    /// 获取访问令牌
    pub async fn get_access_token(&mut self, key: &str) -> Result<String, Box<dyn std::error::Error>> {
        assert!(key == "app_access_token" || key == "tenant_access_token");
        
        let cache_key = "access_token_data";
        let data = self.cache.get(cache_key);
        
        let should_refresh = if let Some(data) = data {
            let update_time = data["update_time"].as_u64().unwrap_or(0);
            let expire = data["expire"].as_u64().unwrap_or(0);
            let current_time = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs();
            
            current_time - update_time > expire * 8 / 10
        } else {
            true
        };
        
        if should_refresh {
            let url = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal";
            let payload = serde_json::json!({
                "app_id": self.app_id,
                "app_secret": self.app_secret
            });
            
            let mut headers = HashMap::new();
            headers.insert("Content-Type".to_string(), "application/json".to_string());
            
            let data = request("POST", url, &headers, Some(&payload)).await?;
            
            let current_time = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs();
            
            let mut token_data = data.clone();
            token_data["update_time"] = serde_json::Value::Number(current_time.into());
            
            self.cache.insert(cache_key.to_string(), token_data);
        }
        
        let data = self.cache.get(cache_key).unwrap();
        Ok(data[key].as_str().unwrap().to_string())
    }
    
    /// 获取带认证的请求头
    pub async fn get_headers(&mut self) -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
        let mut headers = self.headers.clone();
        let access_token = self.get_access_token("app_access_token").await?;
        headers.insert("Authorization".to_string(), format!("Bearer {}", access_token));
        Ok(headers)
    }
    
    /// 获取飞书云空间根目录 token
    pub async fn get_root_folder_token(&mut self) -> Result<String, Box<dyn std::error::Error>> {
        let url = format!("{}/open-apis/drive/explorer/v2/root_folder/meta", self.host);
        let headers = self.get_headers().await?;
        let resp = request("GET", &url, &headers, None).await?;
        Ok(resp["data"]["token"].as_str().unwrap().to_string())
    }
    
    /// 删除用户在云空间内的文件或者文件夹
    /// 
    /// 文件或者文件夹被删除后，会进入用户回收站里。
    /// https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/file/delete
    /// 
    /// # Arguments
    /// * `token` - 文件token
    /// * `kind` - 删除文件类型 file/bitable
    pub async fn remove(&mut self, token: &str, kind: &str) -> Result<Value, Box<dyn std::error::Error>> {
        let url = format!("{}/open-apis/drive/v1/files/{}", self.host, token);
        let headers = self.get_headers().await?;
        
        let mut query_params = HashMap::new();
        query_params.insert("type".to_string(), kind.to_string());
        
        let url_with_params = format!("{}?type={}", url, kind);
        request("DELETE", &url_with_params, &headers, None).await
    }
    
    /// 移动文件或文件夹
    pub async fn move_file(&mut self, token: &str, payload: &Value) -> Result<Value, Box<dyn std::error::Error>> {
        let url = format!("{}/open-apis/drive/v1/files/{}/move", self.host, token);
        let headers = self.get_headers().await?;
        request("POST", &url, &headers, Some(payload)).await
    }
    
    /// 复制文件或文件夹
    pub async fn copy(&mut self, token: &str, payload: &Value) -> Result<Value, Box<dyn std::error::Error>> {
        let url = format!("{}/open-apis/drive/v1/files/{}/copy", self.host, token);
        let headers = self.get_headers().await?;
        request("POST", &url, &headers, Some(payload)).await
    }
    
    /// 上传文件
    pub async fn upload_file(&mut self, file_path: &str, parent_node: &str) -> Result<Value, Box<dyn std::error::Error>> {
        let url = format!("{}/open-apis/drive/v1/files/upload_all", self.host);
        let headers = self.get_headers().await?;
        
        // 读取文件内容
        let file_content = std::fs::read(file_path)?;
        let file_name = std::path::Path::new(file_path)
            .file_name()
            .unwrap()
            .to_str()
            .unwrap();
        
        let payload = serde_json::json!({
            "type": "file",
            "name": file_name,
            "parent_node": parent_node,
            "size": file_content.len(),
            "content": base64::encode(&file_content)
        });
        
        request("POST", &url, &headers, Some(&payload)).await
    }
    
    /// 下载文件
    pub async fn download_file(&mut self, file_token: &str, file_path: &str) -> Result<(), Box<dyn std::error::Error>> {
        let url = format!("{}/open-apis/drive/v1/files/{}/download", self.host, file_token);
        let headers = self.get_headers().await?;
        
        let client = Client::new();
        let mut header_map = reqwest::header::HeaderMap::new();
        for (key, value) in headers {
            header_map.insert(
                reqwest::header::HeaderName::from_bytes(key.as_bytes()).unwrap(),
                reqwest::header::HeaderValue::from_str(&value).unwrap()
            );
        }
        
        let response = client.get(&url)
            .headers(header_map)
            .send()
            .await?;
        
        let content = response.bytes().await?;
        std::fs::write(file_path, content)?;
        
        Ok(())
    }
} 